﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace AI
{
    public partial class Form1 : Form
    {
        string trainimage = @"..\..\..\..\train-images-idx3-ubyte\train-images.bin";
        string trainlabel = @"..\..\..\..\train-labels-idx1-ubyte\train-labels.bin";


        string testimage = @"..\..\..\..\t10k-images-idx3-ubyte\t10k-images.bin";
        string testlabel = @"..\..\..\..\t10k-labels-idx1-ubyte\t10k-labels.bin";


        List<byte[]> images;
        List<byte[]> labels;

        List<float[]> w;            //需要学习的值 
        List<float[]> showw = new List<float[]>();            //需要学习的值 
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        List<byte[]> LoadImage(string filename,ref int width,ref int height)
        {
            System.IO.BinaryReader br = new System.IO.BinaryReader(System.IO.File.Open(filename, System.IO.FileMode.Open));
            br.ReadInt32();
            int count = ReadInt(br);
            width = ReadInt(br);
            height = ReadInt(br);
            List<byte[]> images = new List<byte[]>();

            for (int i = 0; i < count; i++)
            {
                byte[] oneimage = new byte[width * height];
                for (int m = 0; m < width; m++)
                {
                    for (int n = 0; n < height; n++)
                    {
                        oneimage[n * width + m] = br.ReadByte();
                    }
                }
                images.Add(oneimage);
            }
            br.Close();
            
            return images;
        }

        List<byte[]> LoadLabel(string filename)
        {
            System.IO.BinaryReader br2 = new System.IO.BinaryReader(System.IO.File.Open(filename, System.IO.FileMode.Open));
            List<byte[]> labels = new List<byte[]>();
            br2.ReadInt32();
            int itemcount = ReadInt(br2);
            for (int i = 0; i < itemcount; i++)
            {
                byte itemvalue = br2.ReadByte();
                byte[] itemvector = new byte[10];
                for (int j = 0; j < 10; j++)
                {
                    itemvector[j] = 0;
                }
                itemvector[itemvalue] = 1;
                labels.Add(itemvector);
            }
            br2.Close();
            return labels;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int width = 0;
            int height = 0;
            images = LoadImage(trainimage,ref width, ref height);

            for(int i = 0 ; i < 100;i++)
            {
                Bitmap b = new Bitmap(width, height);
                for (int m = 0; m < width; m++)
                {
                    for (int n = 0; n < height; n++)
                    {
                        byte temp = images[i][n * width + m];
                        Color c = Color.FromArgb(temp, temp, temp);
                        b.SetPixel(n, m, c);
                    }
                }
                PictureBox ppp = new PictureBox();
                this.Controls.Add(ppp);
                ppp.Location = new System.Drawing.Point(0 + i * 29, 127);
                ppp.Size = new System.Drawing.Size(28, 28);
                ppp.Image = b;
            }
            labels = LoadLabel(trainlabel);

            if (File.Exists("w.bin"))
            {
                using (System.IO.FileStream fs = new System.IO.FileStream("w.bin", System.IO.FileMode.OpenOrCreate))
                {
                    w = new List<float[]>();
                    System.IO.BinaryReader br3 = new BinaryReader(fs);
                    int count1 = br3.ReadInt32();
                    for (int i = 0; i < count1; i++)
                    {
                        int count2 = br3.ReadInt32();
                        float[] wwss = new float[count2];
                        for (int j = 0; j < count2; j++)
                        {
                            wwss[j] = br3.ReadSingle();
                        }
                        w.Add(wwss);
                    }
                    fs.Close();
                }

            }
            else
            {
                w = new List<float[]>();
                for (int i = 0; i < 10; i++)
                {
                    w.Add(new float[28 * 28]);
                }
            }
            CopyW();
            timer1.Enabled = true;
        }

        double GetTotalJiaoChaShang(List<float[]> w,int[] index)
        {
            double jiaochashang = 0.0f;
            double[] Evidence = new double[10];
            for (int j = 0; j < index.Length; j++)
            {
                for (int i = 0; i < 10; i++)
                {
                    Evidence[i] = GetEvidence(images[index[j]], w[i]);
                }
                //计算softmax
                double totalsoftmax = 0.0f;
                for (int i = 0; i < 10; i++)
                {
                    totalsoftmax += Math.Pow(Math.E, Evidence[i]);
                }
                double[] softmax = new double[10];
                for (int i = 0; i < 10; i++)
                {
                    softmax[i] = Math.Pow(Math.E, Evidence[i]) / totalsoftmax;
                }
                //这个softmax 预测的概率分布
                //labels[j] 为实际分布
                //计算交叉熵 
                for (int i = 0; i < 10; i++)
                {
                    if (softmax[i] < 0.0000001f)
                    {
                        jiaochashang += -labels[index[j]][i] * Math.Log10(0.0000001);
                    }
                    else
                    {
                        jiaochashang += -labels[index[j]][i] * Math.Log10(softmax[i]);
                    }
                }
                if (double.IsNaN(jiaochashang))
                {
                    System.Diagnostics.Debug.Assert(!double.IsNaN(jiaochashang));
                }
            }

            return jiaochashang;
        }

        float GetEvidence(byte[] image,float[] w)
        {
            float total = 0.0f;
            for(int i = 0 ; i < image.Length;i++)
            {
                total += image[i] * w[i];
            }
            return total;
        }

        static int ReadInt(System.IO.BinaryReader br)
        {
            int a = br.ReadByte();
            int b = br.ReadByte();
            int c = br.ReadByte();
            int d = br.ReadByte();
            return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
        }

        private bool _needShowW = false;
        private void timer1_Tick(object sender, EventArgs e)
        {
            lock (showw)
            {
                if (_needShowW)
                {
                    ShowW();
                    _needShowW = false;
                    button4_Click(sender, e);
                }
            }
            progressBar1.Value = Math.Min(progressBar1.Maximum,(int)(GetProgress() * 100.0f));
        }

        List<PictureBox>  wpics = new List<PictureBox>();

        public void ShowW()
        {
            for (int i = 0; i < wpics.Count; i++)
            {
                this.Controls.Remove(wpics[i]);
            }
            float maxw = 0.0f;
            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 28*28; j++)
                {
                    if (Math.Abs(w[i][j]) > maxw)
                    {
                        maxw = Math.Abs(w[i][j]);
                    }
                }
            }
            float d = 256.0f / maxw * 0.95f ;
            for (int i = 0; i < 10; i++)
            {
                Bitmap b = new Bitmap(28, 28);
                for (int m = 0; m < 28; m++)
                {
                    for (int n = 0; n < 28; n++)
                    {
                        float temp = w[i][n * 28 + m];
                        Color c = Color.FromArgb(temp < 0.0f? (byte)(-temp * d) : 0, temp > 0.0f ? (byte)(temp * d) :0 , 0);
                        b.SetPixel(n, m, c);
                    }
                }

                PictureBox ppp = new PictureBox();
                this.Controls.Add(ppp);
                ppp.Location = new System.Drawing.Point(0 + i * 120, 327);
                ppp.Size = new System.Drawing.Size(112, 112);
                ppp.Image = b;
                ppp.SizeMode = PictureBoxSizeMode.StretchImage;
                wpics.Add(ppp);
            }
        }

        public void SaveW()
        {
            using (System.IO.FileStream fs = new System.IO.FileStream("w.bin",System.IO.FileMode.OpenOrCreate))
            {
                System.IO.BinaryWriter bw = new BinaryWriter(fs);
                bw.Write(w.Count);
                for (int i = 0; i < w.Count; i++)
                {
                    bw.Write(w[i].Length);
                    for (int j = 0; j < w[i].Length; j++)
                    {
                        bw.Write(w[i][j]);
                    }
                }
                fs.Close();
            }   
        }

        void ThreadStart()
        {
            while (!hasQuit)
            {
                CalcOnce();
                SaveW();
                //将数据 翻到 客户端 
                CopyW();
            }
        }

        void CopyW()
        {
            lock (showw)
            {
                showw.Clear();
                for (int i = 0; i < w.Count; i++)
                {
                    float[] wwss = new float[w[i].Length];
                    for (int j = 0; j < w[i].Length; j++)
                    {
                        wwss[j] = w[i][j];
                    }
                    showw.Add(wwss);
                }
                _needShowW = true;
            }
        }

        private bool hasQuit = false;

        private int CalcI = 0;
        private int CalcJ = 0;

        float GetProgress()
        {
//            return CalcJ / (28 * 28.0f );
            return CalcI /10.0f + CalcJ/(28*28*10.0f);
        }

        void CalcOnce()
        {
            float dw = 0.00001f;
            Random r = new Random();
            List<int> indexList = new List<int>();

            for (int i = 0; i < 1000; i++)
            {
                int aaaaa = r.Next(0, 5000);
                while (indexList.Contains(aaaaa))
                {
                    aaaaa = r.Next(0, 5000);
                }
                indexList.Add(aaaaa);
            }
            int[] index = indexList.ToArray();

            for (CalcI = 0; CalcI < 10; CalcI++)
            {
                for (CalcJ = 0; CalcJ < 28 * 28; CalcJ++)
                {
                    double jiaochashang = GetTotalJiaoChaShang(w, index);
                    w[CalcI][CalcJ] += dw;
                    double jiaochashang1 = GetTotalJiaoChaShang(w, index);
                    w[CalcI][CalcJ] -= 2 * dw;
                    double jiaochashang2 = GetTotalJiaoChaShang(w, index);
                    if (jiaochashang1 == jiaochashang2)
                    {
                        w[CalcI][CalcJ] += dw;
                    }
                    if (jiaochashang1 < jiaochashang2)
                    {
                        w[CalcI][CalcJ] += 2 * dw;
                    }
                }
            }
        }

        private System.Threading.Thread t;
        private void button2_Click(object sender, EventArgs e)
        {
            t = new System.Threading.Thread(ThreadStart);
            t.Start();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            hasQuit = true;
            if (t != null)
            {
                t.Join();
            }
            this.Close();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            int width = 0;
            int height = 0;

            List<byte[]> testimages = LoadImage(testimage,ref width,ref height);
            List<byte[]> testlables = LoadLabel(testlabel);
            List<float[]> testw = new List<float[]>();
            lock (showw)
            {
                for (int i = 0; i < showw.Count; i++)
                {
                    float[] wwss = new float[showw[i].Length];
                    for (int j = 0; j < showw[i].Length; j++)
                    {
                        wwss[j] = showw[i][j];
                    }
                    testw.Add(wwss);
                }
            }
            int right = 0;
            int wrong = 0;

            for (int i = 0; i < testimages.Count; i++)
            {
                double[] Evidence = new double[10];
                for (int j = 0; j < 10; j++)
                {
                    Evidence[j] = GetEvidence(testimages[i], testw[j]);
                }

                double totalsoftmax = 0.0f;
                for (int j = 0; j < 10; j++)
                {
                    totalsoftmax += Math.Pow(Math.E, Evidence[j]);
                }

                double[] softmax = new double[10];
                for (int j = 0; j < 10; j++)
                {
                    softmax[j] = Math.Pow(Math.E, Evidence[j]) / totalsoftmax;
                }
                int maxj = -1;
                float max = 0.0f;
                for (int j = 0; j < 10; j++)
                {
                    if (softmax[j] > max)
                    {
                        maxj = j;
                        max = (float)softmax[j];
                    }
                }

                int maxlabel = -1;
                for (int j = 0; j < 10; j++)
                {
                    if (testlables[i][j] == 1)
                    {
                        maxlabel = j;
                        break;
                        ;
                    }
                }
                if (maxlabel == maxj)
                {
                    //预估正确;
                    right++;
                }
                else
                {
                    wrong++;
                }
            }
            label1.Text = "正确率" + (right/100.0f).ToString() + "%";
        }
    }
}
